package com.piece.core.framework.util.net;

import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.framework.util.string.StringUtil;
import lombok.Data;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

@Data
public class URLQuery {

    private final Map<CharSequence, CharSequence> query;

    public URLQuery() {
        this(null);
    }

    public URLQuery(Map<? extends CharSequence, ?> queryMap) {
        if (MapUtil.isNotEmpty(queryMap)) {
            this.query = new HashMap<>(queryMap.size());
            this.addAll(queryMap);
        } else {
            this.query = new HashMap(16);
        }
    }

    public static URLQuery of(Map<? extends CharSequence, ?> queryMap) {
        return new URLQuery(queryMap);
    }

    public static URLQuery of(String queryStr, Charset charset) {
        return of(queryStr, charset, true);
    }

    public static URLQuery of(String queryStr, Charset charset, boolean autoRemovePath) {
        return new URLQuery().parse(queryStr, charset, autoRemovePath);
    }

    public URLQuery add(CharSequence key, Object value) {
        this.query.put(key, Convert.toStr(value));
        return this;
    }

    private void addParam(String key, String value, Charset charset) {
        if (null != key) {
            String actualKey = URLDecoder.decode(key, charset);
            this.query.put(actualKey, Convert.toStr(URLDecoder.decode(value, charset), ""));
        } else if (null != value) {
            this.query.put(URLDecoder.decode(value, charset), null);
        }
    }

    public URLQuery addAll(Map<? extends CharSequence, ?> queryMap) {
        if (MapUtil.isNotEmpty(queryMap)) {
            queryMap.forEach(this::add);
        }
        return this;
    }

    public URLQuery parse(String queryStr, Charset charset) throws Exception {
        return this.parse(queryStr, charset, true);
    }

    public URLQuery parse(String queryStr, Charset charset, boolean autoRemovePath) {
        if (!StringUtil.isBlank(queryStr)) {
            int len;
            if (autoRemovePath) {
                len = queryStr.indexOf(63);
                if (len > -1) {
                    queryStr = StringUtil.substring(queryStr, len + 1);
                    if (StringUtil.isBlank(queryStr)) {
                        return this;
                    }
                }
            }

            len = queryStr.length();
            String name = null;
            int pos = 0;

            int i;
            for (i = 0; i < len; ++i) {
                char c = queryStr.charAt(i);
                switch (c) {
                    case '&':
                        try {
                            this.addParam(name, queryStr.substring(pos, i), charset);
                            name = null;
                            if (i + 4 < len && "amp;".equals(queryStr.substring(i + 1, i + 5))) {
                                i += 4;
                            }

                            pos = i + 1;
                            break;
                        } catch (Exception e) {
                        }
                    case '=':
                        if (null == name) {
                            name = queryStr.substring(pos, i);
                            pos = i + 1;
                        }
                }
            }

            this.addParam(name, queryStr.substring(pos, i), charset);
        }
        return this;
    }

    public String build(Charset charset) {
        if (MapUtil.isEmpty(this.query)) {
            return "";
        } else {
            StringBuilder sb = new StringBuilder();
            boolean isFirst = true;
            Iterator var6 = this.query.entrySet().iterator();

            while(var6.hasNext()) {
                if (isFirst) {
                    isFirst = false;
                } else {
                    sb.append("&");
                }

                Map.Entry<CharSequence, CharSequence> entry = (Map.Entry)var6.next();
                CharSequence key = entry.getKey();
                if (null != key) {
                    sb.append(URLEncoder.ALL.encode(Convert.toStr(key, ""), charset));
                    CharSequence value = entry.getValue();
                    if (null != value) {
                        sb.append("=").append(URLEncoder.ALL.encode(Convert.toStr(value, ""), charset));
                    }
                }
            }

            return sb.toString();
        }
    }
}